package com.guodefu.demo.config;

import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;

import java.time.Duration;

/**
 * author:zj
 * Date:2020/4/8
 * Time:14:55
 * 通过重写RedisCache中的put的方法 实现spring cache 自定义key ttl事件
 *
 */
public class CustomizeRedisCache extends RedisCache {

    public CustomizeRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfiguration) {
        super(name, cacheWriter, cacheConfiguration);
    }

//
//    //校验规则：获取时间
//    String REGEX_STR = ".*#\\d+$";
//
//    private static final String Splitter="#";
//    /**
//     * Create new {@link RedisCache}.
//     *
//     * @param name        must not be {@literal null}.
//     * @param cacheWriter must not be {@literal null}.
//     * @param cacheConfig must not be {@literal null}.
//     */
//    protected CustomizeRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
//        super(name, cacheWriter, cacheConfig);
//    }
//
//    @Override
//    public String getName() {
//        String name = super.getName();
//        //是否按照指定的格式
//        if (Pattern.matches(REGEX_STR, name)) {
//            List<String> keyList = Arrays.asList(name.split(Splitter));
//            //获取键值
//            return keyList.get(0);
//        }
//        return name;
//    }
//
//    public Duration getTimeToLive(Object key, @Nullable Object value) {
//        String name = super.getName();
//        //是否按照指定的格式
//        if (Pattern.matches(REGEX_STR, name)) {
//            List<String> keyList = Arrays.asList(name.split(Splitter));
//            //获取TTL 执行时间
//            Long ttl = Long.valueOf(keyList.get(1));
//            return Duration.ofSeconds(ttl);
//        }
//        return getTimeToLive0(key, value);
//    }
//
    /**
     * 重写cache put 逻辑，引入自定义TTL 实现
     * 实现逻辑:
     * 1.通过获取@Cacheable 中的value ,然后根据约定好的特殊字符进行分割
     * 2.从分割结果集中获取设置的TTL 时间并将value 中的，然后给当前缓存设置TTL
     */
    public void putWithCustomTtl(Object key, Object value, Duration timeToLive) {

        Object cacheValue = processAndCheckValue(value);

        byte[] binaryKey = createAndConvertCacheKey(key);
        byte[] binaryValue = serializeCacheValue(cacheValue);

        getCacheWriter().put(getName(), binaryKey, binaryValue, timeToLive);
    }

    private Object processAndCheckValue(Object value) {

        Object cacheValue = preProcessCacheValue(value);

        if (nullCacheValueIsNotAllowed(cacheValue)) {

            String message = String.format("Cache '%s' does not allow 'null' values; Avoid storing null"
                    + " via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null'"
                    + " via RedisCacheConfiguration", getName());

            throw new IllegalArgumentException(message);
        }

        return cacheValue;
    }

    private boolean nullCacheValueIsNotAllowed(Object cacheValue) {
        return cacheValue == null && !isAllowNullValues();
    }

    private byte[] createAndConvertCacheKey(Object key) {
        return serializeCacheKey(createCacheKey(key));
    }

}

